package freespiritsocial.io.user.ui.helper

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.Bitmap
import androidx.core.app.ActivityCompat
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.tasks.OnFailureListener
import com.google.android.libraries.places.api.Places
import com.google.android.libraries.places.api.model.AutocompleteSessionToken
import com.google.android.libraries.places.api.model.Place
import com.google.android.libraries.places.api.net.*
import freespiritsocial.io.user.R
import freespiritsocial.io.user.data.model.bean.PlaceAutocompleteBean
import freespiritsocial.io.user.data.model.bean.PlaceAutocompleteBean.Companion.LOCATION_API
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.catch

@ExperimentalCoroutinesApi
class GoogleApiHelper(
    val context: Context
) {
    private val googleApiClient: PlacesClient

    init {
        Places.initialize(context,context.getString(R.string.google_api_key))
        googleApiClient = Places.createClient(context)
    }

    var callbackFlow: ProducerScope<PlaceAutocompleteBean>? = null
    suspend fun searchPlaces(
        content: String
    ): Flow<PlaceAutocompleteBean> {
        callbackFlow?.cancel()
        return callbackFlow<PlaceAutocompleteBean> {
            callbackFlow?.close()
            callbackFlow = this

            val request =
                findAutocompletePredictionsRequest(content)
            ensureActive()
            googleApiClient.findAutocompletePredictions(request)
                .addOnSuccessListener { response ->
                    if (response.autocompletePredictions.isEmpty()) {
                        this.offerCatching(PlaceAutocompleteBean())
                    }
                    for (prediction in response.autocompletePredictions) {
                        this.offerCatching(
                            PlaceAutocompleteBean(
                                prediction.placeId,
                                prediction.getPrimaryText(null).toString(),
                                prediction.getSecondaryText(null).toString()
                            )
                        )
                    }
                    close()
                }.addOnFailureListener(OnFailureListener {
                    close()
                })
            awaitClose()
        }.catch {
            callbackFlow?.close()
            callbackFlow = null
        }
    }

    fun findCurrentPlace(callback: (Place?) -> Unit) {
        if (ActivityCompat.checkSelfPermission(
                context,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            return
        }
        val placeFields: List<Place.Field> =
            listOf(Place.Field.ID,Place.Field.ADDRESS,Place.Field.LAT_LNG,Place.Field.NAME)
        val request: FindCurrentPlaceRequest = FindCurrentPlaceRequest.newInstance(placeFields)
        val findCurrentPlace = googleApiClient.findCurrentPlace(request)
        findCurrentPlace.addOnCompleteListener { task ->
            try {
                task.result.placeLikelihoods.firstOrNull()?.let {
                    callback(it.place)
                }
            } catch (e: java.lang.Exception) {
            }
        }
    }

    fun findNearbyCurrentPlace(callback: (List<PlaceAutocompleteBean>?) -> Unit) {
        if (ActivityCompat.checkSelfPermission(
                context,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            return
        }
        val placeFields: List<Place.Field> =
            listOf(Place.Field.ID,Place.Field.ADDRESS,Place.Field.LAT_LNG,Place.Field.NAME)
        val request: FindCurrentPlaceRequest = FindCurrentPlaceRequest.newInstance(placeFields)
        val findCurrentPlace = googleApiClient.findCurrentPlace(request)
        findCurrentPlace.addOnCompleteListener { task ->
            try {
                callback(task.result.placeLikelihoods.map {
                    PlaceAutocompleteBean(it.place.id,it.place.name,it.place.address,LOCATION_API)
                })
            } catch (e: java.lang.Exception) {
            }
        }
    }

    fun fetchPlace(placeId: String,callback: (Place) -> Unit) {
        val request = getFetchPlaceRequest(placeId)
        googleApiClient.fetchPlace(request).continueWith<Any> { mTask ->
            if (mTask.isSuccessful) {
                val result = mTask.result
                callback.invoke(result.place)
            }
        }
    }

    fun fetchPhoto(placeId: String,callback: (Bitmap?) -> Unit) {
        val request = getFetchPhotoRequest(placeId)
        googleApiClient.fetchPlace(request).continueWith<Any> { mTask ->
            if (mTask.isSuccessful) {
                val result = mTask.result
                val metada = result.place.photoMetadatas
                if (metada == null || metada.isEmpty()) {
                    callback(null)
                } else {
                    val photoMetadata = metada.first()
                    // Create a FetchPhotoRequest.
                    val photoRequest = FetchPhotoRequest.builder(photoMetadata)
                        .setMaxWidth(500) // Optional.
                        .setMaxHeight(300) // Optional.
                        .build()

                    googleApiClient.fetchPhoto(photoRequest)
                        .addOnSuccessListener { fetchPhotoResponse: FetchPhotoResponse ->
                            callback(fetchPhotoResponse.bitmap)
                        }.addOnFailureListener { exception: Exception ->
                            if (exception is ApiException) {
                                callback(null)
                            }
                        }
                }
            }
        }
    }


    private fun getFetchPlaceRequest(placeId: String): FetchPlaceRequest {
        return FetchPlaceRequest.builder(
            placeId,
            listOf(Place.Field.ID,Place.Field.ADDRESS,Place.Field.ADDRESS_COMPONENTS,Place.Field.LAT_LNG,Place.Field.NAME,Place.Field.TYPES)
        ).setSessionToken(AutocompleteSessionToken.newInstance())
            .build()
    }

    private fun getFetchPhotoRequest(placeId: String): FetchPlaceRequest {
        return FetchPlaceRequest.newInstance(
            placeId,
            listOf(Place.Field.PHOTO_METADATAS)
        )
    }


    private fun findAutocompletePredictionsRequest(constraint: CharSequence): FindAutocompletePredictionsRequest {
        return FindAutocompletePredictionsRequest.builder()
            .setSessionToken(AutocompleteSessionToken.newInstance())
            .setQuery(constraint.toString())
            .build()
    }

    fun onDestroy() {
        callbackFlow?.close()
    }

    private fun <T> SendChannel<T>.offerCatching(element: T): Boolean {
        return runCatching { offer(element) }.getOrDefault(false)
    }
}

